package service

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/hyperledger/fabric-chaincode-go/shim"
	"github.com/hyperledger/fabric-samples/asset-kx/chaincode-go/chaincode/asset/api"
	"github.com/hyperledger/fabric-samples/asset-kx/chaincode-go/chaincode/asset/service"
	"github.com/hyperledger/fabric-samples/asset-kx/chaincode-go/chaincode/assetloan/api"
	ledgerapi "github.com/hyperledger/fabric-samples/asset-kx/chaincode-go/chaincode/ledger-api"
	"github.com/hyperledger/fabric-samples/asset-kx/chaincode-go/chaincode/support"
	"github.com/shopspring/decimal"
)

type SmartAssetLoanContract struct {
	AssetContract assetapi.AssetContractApi `inject:"AssetContractApi"`
}

func (s *SmartAssetLoanContract) delAssetLoanRelAndUnFreezeAsset(ctx ledgerapi.TransactionContextInterface, assetLoanResult shim.StateQueryIteratorInterface) error {
	stateDb := ctx.GetStateDb()
	assetUnFreezeAmount := make(map[string]decimal.Decimal)
	err := support.ProcessStateQueryResult(assetLoanResult, func(key string, value []byte) error {
		
		err := ctx.GetRelatedKeyStub().DelState(key)
		if err != nil {
			return err
		}
		assetLoan, err := assetloanapi.Unmarshal(value)
		if err != nil {
			return err
		}
		loanAmountD, err := decimal.NewFromString(assetLoan.LoanAmount)
		if err != nil {
			return err
		}
		assetUnFreezeAmount[assetLoan.AssetNo] = loanAmountD.Add(assetUnFreezeAmount[assetLoan.AssetNo])
		return nil
	})
	if err != nil {
		return err
	}

	for assetNo, unfreezeAmount := range assetUnFreezeAmount {
		asset, err := s.AssetContract.ReadAsset(ctx, assetNo)
		if err != nil {
			return err
		}
		err = asset.UnFreeze(unfreezeAmount.String())
		if err != nil {
			return err
		}
		err = stateDb.Update(asset)
		if err != nil {
			return err
		}
	}
	return nil
}

func (s *SmartAssetLoanContract) ReadLoanRelatedAssets(ctx ledgerapi.TransactionContextInterface, loanNo string) ([]assetapi.Asset, error) {
	stub := ctx.GetStub()

	// queryString := fmt.Sprintf(`{"selector":{"DocType":"AssetLoan","LoanNo":"%s"}}`, loanNo)
	// result, err := stub.GetQueryResult(queryString)
	// if err != nil {
	// 	return nil, err
	// }

	result, err := ctx.GetRelatedKeyStub().GetStateByPartialRelatedCompositeKey(service.LoanAssetKeyType, []string{loanNo})
	if err != nil {
		return nil, err
	}

	var assetNos []string
	err = support.ProcessStateQueryResult(result, func(key string, value []byte) error {
		_, keyArr, err := stub.SplitCompositeKey(key)
		if err != nil {
			return err
		}
		if len(keyArr) > 1 {
			assetNos = append(assetNos, keyArr[0])
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	var assets []assetapi.Asset
	for _, assetNo := range assetNos {
		asset, err := s.AssetContract.ReadAsset(ctx, assetNo)
		if err != nil {
			return nil, err
		}
		assets = append(assets, asset)
	}
	return assets, nil
}
func (s *SmartAssetLoanContract) AssetLoanRelation(ctx ledgerapi.TransactionContextInterface, assetNo string, loanNo string, loanAmount string) error {
	
	assetDb, err := s.AssetContract.ReadAsset(ctx, assetNo)
	if err != nil {
		return err
	}

	db := ctx.GetStateDb()
	err = assetDb.Freeze(loanAmount)
	if err != nil {
		return err
	}
	if err = db.Update(assetDb); err != nil {
		return err
	}

	stub := ctx.GetStub()
	uTxid, err := support.GenUtxid(stub, ctx)
	if err != nil {
		return err
	}

	key, err := stub.CreateCompositeKey(service.AssetLoanKeyType, []string{assetNo, loanNo, uTxid})
	if err != nil {
		return err
	}
	err = stub.SetEvent("AssetLoanRelation", []byte(key))
	if err != nil {
		return err
	}
	assetLoan := &assetloanapi.AssetLoan{
		AssetNo:    assetNo,
		LoanNo:     loanNo,
		LoanAmount: loanAmount,
		DocType:    "AssetLoan",
	}
	alb, err := json.Marshal(assetLoan)
	if err != nil {
		return err
	}
	releatedKey, err := stub.CreateCompositeKey(service.LoanAssetKeyType, []string{loanNo, assetNo, uTxid})
	if err != nil {
		return err
	}

	return ctx.GetRelatedKeyStub().PutState(key, alb, releatedKey)
}

func (s *SmartAssetLoanContract) AssetLoanDeRelation(ctx ledgerapi.TransactionContextInterface, assetNo string, loanNo string) error {
	if strings.TrimSpace(assetNo) == "" || strings.TrimSpace(loanNo) == "" {
		return fmt.Errorf("invalid param, assetNo and loanNo must be no empty")
	}
	stub := ctx.GetStub()
	result, err := stub.GetStateByPartialCompositeKey(service.AssetLoanKeyType, []string{assetNo, loanNo})
	if err != nil {
		return err
	}
	defer result.Close()
	err = stub.SetEvent("AssetLoanDeRelation", []byte(assetNo+"-"+loanNo))
	if err != nil {
		return err
	}
	return s.delAssetLoanRelAndUnFreezeAsset(ctx, result)
}

//1、del(asset~loan)；2、unfreeze(asset)
func (s *SmartAssetLoanContract) LoanPayed(ctx ledgerapi.TransactionContextInterface, loanNo string) error {
	if strings.TrimSpace(loanNo) == "" {
		return fmt.Errorf("invalid param, loanNo must be no empty")
	}
	stub := ctx.GetStub()

	// queryString := fmt.Sprintf(`{"selector":{"DocType":"AssetLoan","LoanNo":"%s"}}`, loanNo)
	// result, err := stub.GetQueryResult(queryString)
	// if err != nil {
	// 	return err
	// }
	// defer result.Close()

	result, err := ctx.GetRelatedKeyStub().GetStateByPartialRelatedCompositeKey(service.LoanAssetKeyType, []string{loanNo})
	if err != nil {
		return err
	}
	defer result.Close()

	err = stub.SetEvent("LoanPayed", []byte(loanNo))
	if err != nil {
		return err
	}
	return s.delAssetLoanRelAndUnFreezeAsset(ctx, result)
}

func (s *SmartAssetLoanContract) ReadAssetRelatedLoans(ctx ledgerapi.TransactionContextInterface, assetNo string) ([]*assetloanapi.AssetLoan, error) {
	stub := ctx.GetStub()
	compositeKeyIte, err := stub.GetStateByPartialCompositeKey(service.AssetLoanKeyType, []string{assetNo})
	if err != nil {
		return nil, err
	}
	defer compositeKeyIte.Close()
	var assetLoans = make([]*assetloanapi.AssetLoan, 0)
	for compositeKeyIte.HasNext() {
		next, err := compositeKeyIte.Next()
		if err != nil {
			return nil, err
		}
		assetLoan, err := assetloanapi.Unmarshal(next.Value)
		if err != nil {
			return nil, err
		}
		assetLoans = append(assetLoans, assetLoan)
	}
	return assetLoans, nil
}
